<?php
$loader->requireOnce('ordo/TicketItem.class.php');
$loader->requireOnce('includes/PatientQueue/CashierQueue.class.php');
$loader->requireOnce('includes/PatientQueue/EncounterQueue.class.php');
$loader->requireOnce('includes/PatientQueue/InProgressQueue.class.php');
$loader->requireOnce('includes/PatientQueue/WaitingQueue.class.php');
$loader->requireOnce('includes/PatientQueue/PatientQueueException.class.php');

/**
 * At a point of time, one ticket_item belongs to only one queue
 * a couple (ticket_item_id, queue_id) set up the primary key of queue_item table
 */
abstract class AbsQueue {
	/**
	 * Queue ENUM constants
	 *
	 */
	const CASHIER = "cashier";
	const ENCOUNTER = "encounter";
	const WAITING = "waiting";
	const IN_PROGRESS = "in_progress";

	/**
	 * Render this queue using ajax. Each different instance has
	 * difference view based on this method
	 *
	 * @param int $roomId
	 */
	public abstract function renderQueueAjax($roomId,$departmentId);
	
	/**
	 * Render the quick queue using ajax. This type of special queue is
	 * use embedded in some page
	 * 
	 * @param int $roomId
	 * @param int $itemPerPage
	 * @param int $page
	 */
	public abstract function renderQuickQueueAjax($roomId, $itemPerPage, $page, $holderId = 0);

	/**
	 * Get queueId from database of this queue
	 *
	 * @throw PatientQueueException if this queue instance is not exist in database
	 */
	public abstract function getQueueId();

	/**
	 * Static method to get a queue instance
	 *
	 * @param String $type
	 * @return AbsInstance instance
	 *
	 * @throw PatientQueueException if this queue is not available
	 */
	public static function getQueue($type) {
		switch ($type) {
			case self::CASHIER:
				return new CashierQueue($type);
			case self::ENCOUNTER:
				return new EncounterQueue($type);
			case self::WAITING:
				return new WaitingQueue($type);
			case self::IN_PROGRESS:
				return new InProgressQueue($type);
			default:
				throw new PatientQueueException("Queue $type is not supported");
		}
	}

	/**
	 * Add a patient to this queue
	 *
	 * @param int $patientId
	 * @param int $ticketItemId service_id
	 * @param int $roomId
	 *
	 */
	public function addTicketItem($ticketItemId){
		$queueId = $this->getQueueId();
		$this->addItemToQueue($ticketItemId, $queueId);
	}

	/**
	 * Remove a patient from current queue
	 *
	 * @param int $patientId
	 * @param int $ticketItemId service_id
	 * @param int $roomId
	 */
	public static function removeTicketItem($ticketItemId){
		self::removeItemFromQueue($ticketItemId);
	}

	/**
	 * Move a ticket iem from current queue to another queue
	 * 1. If ticketItemId does not exist in current queue, do nothing
	 * 2. IF destination queue is CASHIER
	 * 		2.1 Remove from current queue
	 * 		2.2 Add to Cashier queue
	 * 		Since the room_id in ticketItem doesn't refer to Cashier room,
	 * 		we need some special check in add action.
	 * 		RETURN;
	 * 3. The mothod automatic update the status of ticket item relying on
	 * the destination queue. Except the case of source queue is CASHIER
	 * 		IF destination queue is IN_PROGRESS , change ticket item status to "in_progress"
	 * 		IF destination queue is WAITING , change ticket item status to "waiting"
	 *
	 * @param int $ticketItemId
	 * @param string $destQueue		destination queue
	 *
	 * @return false if the ticket item is already exist in the destination queue
	 * 		   true if the ticket item is moved successfully to the destination queue
	 *
	 * @throw PatientQueueException
	 * 		1. destQueue is not supported
	 * 		2. invalid move (destination queue is different from CASHIER, IN_PROGRESS, WAITING)
	 */
	public static function moveTicketItemToQueue($ticketItemId, $destQueue) {
		$queueItem = Celini::newORDO('QueueItem');

		$destQueueObj = self::getQueue($destQueue);
		$destQueueId = $destQueueObj->getQueueId();

		// prevent hard refresh page by pressing F5
		if ($queueItem->isExistant($ticketItemId, $destQueueId)) {
			return false;
		}

		// Special case of move: CASHIER->IN_PROGRESS
		$srcQueue = $queueItem->getSourceQueue($ticketItemId);
		if ($srcQueue == self::CASHIER && $destQueue == self::IN_PROGRESS) {
			$queueItem->updateQueue($ticketItemId, $destQueueId);
			return true;
		}

		// Special case of move: IN_PROGRESS->WAITING & WAITING->IN_PROGRESS
		if ( ($srcQueue == self::IN_PROGRESS && $destQueue == self::WAITING)
				|| ($srcQueue == self::WAITING && $destQueue == self::IN_PROGRESS)) {
			$queueItem->updateQueue($ticketItemId, $destQueueId);
		}
		// The other cases, do the remove and add action separately
		else {
			self::removeItemFromQueue($ticketItemId);
			self::addItemToQueue($ticketItemId, $destQueueId);
		}

		// When dest queue is CASHIER or ENCOUNTER, don't change status
		if ($destQueue == AbsQueue::CASHIER
			/*|| $destQueue == AbsQueue::ENCOUNTER*/){
			return true;
		}

		// update ticket_item status
		$newStatus = self::getNewStatus($destQueue);
		$ticketItem = Celini::newORDO('TicketItem');
		$ticketItem->changeStatus($ticketItemId, $newStatus);
		return true;
	}

	//////////////////// PRIVATE METHODS /////////////////////

	/**
	 * Add patient to queue
	 *
	 * @param int $patientId
	 * @param int $ticketItemId
	 * @param int $queueId
	 */
	private function addItemToQueue($ticketItemId, $queueId) {
		$queueItem = Celini::newORDO('QueueItem');
		$queueItem->add($ticketItemId, $queueId);
	}

	/**
	 * Remove ticket item from a queue
	 *
	 * @param int $ticketItemId
	 */
	private static function removeItemFromQueue($ticketItemId) {
		if (empty($ticketItemId)) {
			return;
		}
		$queueItem = Celini::newORDO('QueueItem');
		$queueItem->delete($ticketItemId);
	}

	/**
	 * Get new status of ticket item relying on the destination queue
	 *
	 * @param string $destQueue
	 *
	 * @throw PatientQueueException
	 *    1. Invalid move (destination queue is not CASHIER, IN_PROGRESS, WAITING);
	 */
	private static function getNewStatus($destQueue) {
		switch ($destQueue) {
			case self::IN_PROGRESS:
				return TicketItem::IN_PROGRESS;
			case self::WAITING:
				return TicketItem::WAITING;
			case self::ENCOUNTER:
				return TicketItem::OPEN;
			default:
				throw new PatientQueueException("Invalid move, source queue: " . $this->getType() .
					", destination queue: " + $destQueue);
		}
	}
}
?>